feat(kubernetes): add sidecar supervisor topology#2076
Conversation
|
Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually. Contributors can view more details about this message here. |
|
🌿 Preview your docs: https://nvidia-preview-pr-2076.docs.buildwithfern.com/openshell |
|
Label |
4f981c2 to
8c9ae53
Compare
Some comments with help of agent1. Central trust boundary — the workload can use the sandbox's gateway identity . 2. nftables fence only covers TCP/UDP. 3. 4. Doc drift in the debug skill. |
e00a00b to
ed858eb
Compare
Addressed this by moving the supervisor and sandbox to run with different GIDs. Updated the other areas with clarifying comments. |
|
Deployed this branch on an OpenShift 4.22 cluster (K8s 1.35) with Root causeIn
Step 2 runs as root with Note the state dir was already changed to |
|
@mrunalp I reproduced locally and pushed a fix. Please have a look when you get a chance! |
Add the Kubernetes sidecar supervisor topology, its Helm/Skaffold configuration, topology documentation, and sidecar e2e matrix coverage. Skip root-only sandbox identity rewriting when process enforcement is network-only so the low-permission sidecar process container can start successfully. Signed-off-by: Taylor Mutch <taylormutch@gmail.com>
Signed-off-by: Taylor Mutch <taylormutch@gmail.com>
Signed-off-by: Taylor Mutch <taylormutch@gmail.com>
Signed-off-by: Taylor Mutch <taylormutch@gmail.com>
Signed-off-by: Taylor Mutch <taylormutch@gmail.com>
Signed-off-by: Taylor Mutch <taylormutch@gmail.com>
Signed-off-by: Taylor Mutch <taylormutch@gmail.com>
Signed-off-by: Taylor Mutch <taylormutch@gmail.com>
Signed-off-by: Taylor Mutch <taylormutch@gmail.com>
3d80066 to
254cdbe
Compare
| # staging directory before the image build job runs. | ||
|
|
||
| FROM scratch AS supervisor | ||
| FROM alpine:3.22 AS supervisor |
There was a problem hiding this comment.
Making a note that this might break the podman implementation which expects a scratch container. I might be wrong here though. I would expect E2E tests to shake this out.
| supervisor_topology = {{ .Values.supervisor.topology | default "combined" | quote }} | ||
| process_enforcement = {{ .Values.supervisor.processEnforcement | default "network-only" | quote }} |
There was a problem hiding this comment.
Can the supervisor_topology automatically determine how process_enforcement is configured? Configuring topology and process enforcement seems to leak implementation details of the topology.
| case "${selected_engine}" in | ||
| docker|podman) | ||
| export CONTAINER_ENGINE="${selected_engine}" | ||
| return | ||
| ;; | ||
| *) | ||
| echo "ERROR: CONTAINER_ENGINE=${CONTAINER_ENGINE} is invalid; expected docker or podman" >&2 | ||
| exit 2 | ||
| ;; | ||
| esac |
There was a problem hiding this comment.
why is there a docker branch here?
Looks like you've rebased now since that's been merged? |
| /// Supervisor pod/runtime topology. Kubernetes sidecar mode sets this to | ||
| /// `"sidecar"`; the default combined supervisor path omits it. | ||
| pub const SUPERVISOR_TOPOLOGY: &str = "OPENSHELL_SUPERVISOR_TOPOLOGY"; | ||
|
|
||
| /// Network enforcement backend selected by the compute driver. | ||
| pub const NETWORK_ENFORCEMENT_MODE: &str = "OPENSHELL_NETWORK_ENFORCEMENT_MODE"; | ||
|
|
||
| /// Process enforcement mode selected by the compute driver. | ||
| /// | ||
| /// The default when unset is `"full"`, where the process supervisor enforces | ||
| /// filesystem/process policy before spawning workloads. Kubernetes sidecar | ||
| /// topology sets this to `"network-only"` so the process wrapper can run as | ||
| /// the sandbox UID without Linux capabilities while preserving SSH/session | ||
| /// behavior. | ||
| pub const PROCESS_ENFORCEMENT_MODE: &str = "OPENSHELL_PROCESS_ENFORCEMENT_MODE"; | ||
|
|
||
| /// Whether network policy evaluation must bind requests to the peer binary. | ||
| /// | ||
| /// The default when unset is `"required"`. Kubernetes sidecar experiments may | ||
| /// set this to `"relaxed"` to enforce endpoint and L7 policy without per-binary | ||
| /// `/proc` identity binding. | ||
| pub const NETWORK_BINARY_IDENTITY: &str = "OPENSHELL_NETWORK_BINARY_IDENTITY"; | ||
|
|
||
| /// File written by the network supervisor when sidecar networking is ready. | ||
| pub const SUPERVISOR_READY_FILE: &str = "OPENSHELL_SUPERVISOR_READY_FILE"; | ||
|
|
||
| /// File written by the process supervisor with the workload entrypoint PID and | ||
| /// read by the network sidecar for process/binary-bound network policy checks. | ||
| pub const ENTRYPOINT_PID_FILE: &str = "OPENSHELL_ENTRYPOINT_PID_FILE"; | ||
|
|
||
| /// Local protobuf policy snapshot written by the network sidecar and read by | ||
| /// the process-only supervisor in Kubernetes sidecar topology. | ||
| pub const SIDECAR_POLICY_SNAPSHOT_FILE: &str = "OPENSHELL_SIDECAR_POLICY_SNAPSHOT_FILE"; | ||
|
|
||
| /// Local provider environment snapshot written by the network sidecar and read | ||
| /// by the process-only supervisor in Kubernetes sidecar topology. | ||
| pub const SIDECAR_PROVIDER_ENV_SNAPSHOT_FILE: &str = "OPENSHELL_SIDECAR_PROVIDER_ENV_SNAPSHOT_FILE"; | ||
|
|
||
| /// Optional TLS server name override used when connecting to the gateway. | ||
| pub const GATEWAY_TLS_SERVER_NAME: &str = "OPENSHELL_GATEWAY_TLS_SERVER_NAME"; | ||
|
|
||
| /// Directory where the network supervisor writes the proxy CA files consumed | ||
| /// by workload child processes. | ||
| pub const PROXY_TLS_DIR: &str = "OPENSHELL_PROXY_TLS_DIR"; | ||
|
|
There was a problem hiding this comment.
why do these constants need to be in core?
| install_sidecar_iptables_legacy_bypass_rules(proxy_uid).map_err(|iptables_error| { | ||
| miette::miette!( | ||
| "sidecar nft ruleset load failed: {nft_error}; sidecar iptables-legacy fallback failed: {iptables_error}" | ||
| ) | ||
| }) |
There was a problem hiding this comment.
(from agent review)
When nftables loading fails on a dual-stack pod, this fallback reports success after installing only IPv4 iptables-legacy rules. IPv6 TCP/UDP traffic remains unrestricted, allowing workloads to bypass the sidecar proxy and network policy entirely. Install equivalent ip6tables-legacy rules or fail closed when IPv6 cannot be fenced.
Summary
Adds the Kubernetes
sidecarsupervisor topology after the combined-topology base from #2074.The default
combinedtopology remains unchanged, andsidecaris opt-in.sidecarmoves pod-level network enforcement and gateway forwarding into a dedicated supervisor sidecar. The agent container can run as the resolved sandbox UID/GID withrunAsNonRoot, no privilege escalation, and all Linux capabilities dropped.This draft intentionally includes the prerequisite UID/GID commits from #1973 for now. We expect to rebase those out after that work lands separately.
Runtime validation status:
sidecarhas been validated on GKE Autopilot clusters created withgcloud container clusters create-auto --workload-policies=allow-net-admin; the Google Cloud CLI reference listsallow-net-adminas the supported Autopilot workload policy: https://docs.cloud.google.com/sdk/gcloud/reference/container/clusters/create-auto#--workload-policiessidecaris experimental with Kata Containers.sidecaris known to fail with gVisor because it depends on pod-local network rule setup.Sidecar mode preserves gateway session and SSH behavior, but intentionally runs the process supervisor in network-only mode. Filesystem policy, process privilege dropping, and process/binary identity checks are not applied in this mode.
Related Issue
References #1827, #981, #899, #1305.
Related PRs: #1973, #2074, #2016.
Changes
sidecarsupervisor topology and the relatedprocessEnforcementandproxyUidconfiguration.network-onlybehavior for sidecar mode while keeping SSH/session relay behavior intact.Testing
git diff --check origin/main..feat/kubernetes-sidecar-topology-v2cargo check -p openshell-driver-kubernetes -p openshell-sandbox -p openshell-supervisor-process -p openshell-supervisor-networkcargo test -p openshell-driver-kubernetes --libcargo test -p openshell-supervisor-process --libmise run helm:testmarkdownlint-cli2 docs/kubernetes/topology.mdx docs/kubernetes/setup.mdxruby -e 'require "yaml"; YAML.load_file(".github/workflows/branch-e2e.yml"); puts "ok"'mise run rust:lintHELM_K3S_LB_HOST_PORT=18080 mise run e2e:kubernetes:sidecar--workload-policies=allow-net-admin.Checklist